﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Configuration;

namespace ConversionInterface
{
    public class ConvertMedia : IDisposable
    {
        #region Member Data
        private Process process;
        private TextWriter logWriter;
        private MediaFile file;
        private StreamReader processOutput;
        private bool disposed;
        #endregion

        #region Enumerations, Delegates, EventArgs, Events, IDisposable
        public enum ConversionTarget
        {
            iPhone,
            iPodTouch,
            PSP
        }
        public class ConversionEventArgs : System.EventArgs
        {
            public MediaFile MediaFile { get; set; }
        }
        public event OnConversionComplete ConversionComplete;
        public event OnConversionPercentageChange ConversionPercentageChanged;
        public event OnConversionStageChange ConversionStageChanged;
        public delegate void OnConversionPercentageChange(object sender, ConversionEventArgs e);
        public delegate void OnConversionStageChange(object sender, ConversionEventArgs e);
        public delegate void OnConversionComplete(object sender, ConversionEventArgs e);
        private void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    if (process != null)
                    {
                        Stop();
                        if (logWriter != null)
                            logWriter.Close();

                        if (processOutput != null)
                            processOutput.Close();
                    }
                }
            }
            this.disposed = true;
        }
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
        public ConvertMedia()
        {
            StreamWriter writer = new StreamWriter("log.txt");
            writer.AutoFlush = true;
            this.logWriter = writer;
        }
        #endregion

        public void Stop()
        {
            try
            {
                process.Kill();
                Process[] encoders = Process.GetProcessesByName("mencoder");
                foreach (Process p in encoders)
                {
                    p.Kill();
                }
            }
            catch { }
        }
        public void ConvertMediaFile(object o)
        {
           
            string script = "";
            StringBuilder parameters = new StringBuilder();
            file = (MediaFile)o;
            Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

            string outputDirectory = config.AppSettings.Settings["OutputDirectory"].Value;
            if (outputDirectory.Trim() == "")
            {
                outputDirectory = "Converted Videos";
            }

            parameters.Append("\"");
            parameters.Append(file.InputFilename);
            parameters.Append("\" ");

            if (outputDirectory.Trim() != "")
            {
                parameters.Append("\"");
                parameters.Append(outputDirectory);
                parameters.Append("\\");
                parameters.Append(file.OutputFilename);
                parameters.Append("\" ");
            }
            else
            {
                parameters.Append("\"");
                parameters.Append(file.OutputFilename);
                parameters.Append("\" ");
            }
           
            if(file.TargetDevice == ConversionTarget.iPhone || file.TargetDevice == ConversionTarget.iPodTouch)
            {
                switch(file.Type)
                {
                    case MediaFile.MediaType.MPEG4:
                        script = "scripts\\iphone_mp4.bat";
                        break;
                    case MediaFile.MediaType.AVI:
                        script = "scripts\\iphone_avi.bat";
                        break;
                    case MediaFile.MediaType.MKV:
                        script = "scripts\\iphone_mkv.bat";
                        parameters.Append(file.ChosenSub.TrackNumber);
                        parameters.Append(" ");
                        parameters.Append(file.ChosenAudio.TrackNumber);
                        break;
                    case MediaFile.MediaType.OGG:
                        script = "scripts\\iphone_ogg.bat";
                        parameters.Append(file.ChosenSub.Language.ToString());
                        parameters.Append(" ");
                        parameters.Append(file.ChosenAudio.TrackNumber);
                        break;
                }
            }
            else if (file.TargetDevice == ConversionTarget.PSP)
            {
                switch (file.Type)
                {
                    case MediaFile.MediaType.MKV:
                        script = "scripts\\psp_mkv.bat";
                        parameters.Append(file.ChosenSub.TrackNumber);
                        parameters.Append(" ");
                        parameters.Append(file.ChosenAudio.TrackNumber);
                        break;
                }
            }

            if (String.IsNullOrEmpty(script))
                throw new ArgumentException("This combination of Target Device and Video Type is not supported");



            RunMEncoderScript(script, parameters.ToString());
        }
        /// <summary>
        /// Gets the equivalent language.  Takes an enumeration, and returns the proper flag used by MEncoder for language choosing
        /// </summary>
        /// <param name="language">The language.</param>
        /// <returns></returns>
        private string GetEquivalentLanguage(MediaFile.MediaLanguage language)
        {
            switch (language)
            {
                case MediaFile.MediaLanguage.English:
                    return "eng";
                case MediaFile.MediaLanguage.French:
                    return "fre";
                case MediaFile.MediaLanguage.Japanese:
                    return "jap";
                case MediaFile.MediaLanguage.Spanish:
                    return "esp";
                case MediaFile.MediaLanguage.Undefined:
                    return "und";
                default:
                    return "none";
            }
        }
        /// <summary>
        /// Runs the .bat file specified by 'script', receiving the specified parameters
        /// </summary>
        /// <param name="script">The script.</param>
        /// <param name="parameters">The parameters.</param>
        private void RunMEncoderScript(string script, string parameters)
        {
            ProcessStartInfo startInfo = new ProcessStartInfo(script, parameters);
            startInfo.RedirectStandardOutput = true;
            startInfo.CreateNoWindow = true;
            startInfo.UseShellExecute = false;
            
            if (this.process != null && !this.process.HasExited)
                throw new ArgumentException("Already a process running!");

            this.process = new Process();
            process.StartInfo = startInfo;
            process.EnableRaisingEvents = true;          
            process.Exited += new EventHandler(process_Exited);
            
            if (process.Start())
            {
                this.processOutput = process.StandardOutput;
                while (!this.processOutput.EndOfStream)
                {
                    string line = this.processOutput.ReadLine();
          
                    this.logWriter.WriteLine(line);
                    if (line.StartsWith("Pos"))
                    {
                        //on lines starting with "Pos:"  we look for the ( #%) substring and raise an event so the UI can update the percent complete
                        int i, j;
                        i = j = 0;
                        i = line.IndexOf('(');
                        if (i > 0)
                            j = line.IndexOf(')', i);
                        if (j > 0)
                        {
                            string percent = line.Substring(i + 1, j - i - 2);
                            int iPercent = Convert.ToInt32(percent.Trim());
                            this.file.PercentComplete = iPercent;
                            ConversionEventArgs args = new ConversionEventArgs();
                            args.MediaFile = this.file;
                            ConversionPercentageChanged(this, args);
                        }
                    }
                    else if (line.StartsWith("Stage:"))
                    {
                        string stage = line.Substring(6).Trim();
                        ConversionEventArgs args = new ConversionEventArgs();
                        args.MediaFile = this.file;
                        args.MediaFile.StageInfo = stage;
                        ConversionStageChanged(this, args);
                    }
                }

            }
            else
            {
                throw new Exception("For unknown reasons, MEncoder could not be started");
            }
        }

        /// <summary>
        /// Gets call when a .bat script is finished executing.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        void process_Exited(object sender, EventArgs e)
        {
            Process process = (Process)sender;
            int exitCode = process.ExitCode;
            ConversionEventArgs args = new ConversionEventArgs();
            args.MediaFile = this.file;
            if (exitCode == 0)
            {
                this.file.PercentComplete = 100;
                ConversionComplete(this, args);
            }
            else
            {
                ConversionComplete(this, args);
            }
        }
    }
}
